package jmine.tec.rpc.server.base;

import jmine.tec.rpc.RPCMessages;
import jmine.tec.rpc.common.RPCException;
import jmine.tec.rpc.common.metadata.RestServiceMetadata;
import jmine.tec.rpc.common.serialization.Serializer;
import jmine.tec.rpc.server.comm.RestServiceStub;
import jmine.tec.rpc.server.provider.ServerRestServiceDiscovery;
import jmine.tec.rpc.server.provider.ServerRestServiceProvider;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;

/**
 * Factory de RestService. Cria o RestServiceStub que irá adaptar a implementação ao RestLikeServlet e registra no provedor de rest
 * services.
 * 
 * @author lundberg
 */
public class RestServiceFactory implements FactoryBean, InitializingBean {

    private ServerRestServiceProvider provider;

    private Serializer serializer;

    private Class<?> service;

    private Object implementor;

    private String prefix;

    private RestServiceStub<?> stub;

    private ServerRestServiceDiscovery discovery;

    /**
     * {@inheritDoc}
     */
    public Object getObject() throws Exception {
        return this.stub;
    }

    /**
     * {@inheritDoc}
     */
    public boolean isSingleton() {
        return true;
    }

    /**
     * {@inheritDoc}
     */
    public Class getObjectType() {
        return RestServiceStub.class;
    }

    /**
     * {@inheritDoc}
     */
    public void afterPropertiesSet() throws Exception {
        this.initializeStub();
        this.registerIntoProvider();
        this.registerIntoDiscovery();
    }

    /**
     * Registra a implementação no provider
     */
    private void registerIntoProvider() {
        this.provider.register(this.findServiceClass(), this.implementor);
    }

    /**
     * Registra a implementação no serviço de descoberta
     */
    private void registerIntoDiscovery() {
        this.discovery.register(this.findServiceClass(), this.implementor, this.prefix);
    }

    /**
     * Inicialize o stub.
     */
    @SuppressWarnings("unchecked")
    private void initializeStub() {
        if (this.stub != null) {
            return;
        }
        this.stub = new RestServiceStub(this.serializer, this.findServiceClass(), this.implementor, this.prefix);
    }

    /**
     * Busca a interface do serviço implementado
     * 
     * @return class
     */
    private Class findServiceClass() {
        if (this.service != null) {
            return this.service;
        }
        for (Class<?> candidate : this.implementor.getClass().getInterfaces()) {
            if (RestServiceMetadata.isService(candidate)) {
                return candidate;
            }
        }
        throw new RPCException(RPCMessages.IMPLEMENTOR_DOES_NOT_IMPLEMENT_ANY_SERVICE.create(this.getClass().getName()));
    }

    /**
     * @param provider the provider to set
     */
    public void setProvider(ServerRestServiceProvider provider) {
        this.provider = provider;
    }

    /**
     * @param serializer the serializer to set
     */
    public void setSerializer(Serializer serializer) {
        this.serializer = serializer;
    }

    /**
     * @param service the service to set
     */
    public void setService(Class<?> service) {
        this.service = service;
    }

    /**
     * @param implementor the implementor to set
     */
    public void setImplementor(Object implementor) {
        this.implementor = implementor;
    }

    /**
     * @param stub the stub to set
     */
    public void setStub(RestServiceStub<?> stub) {
        this.stub = stub;
    }

    /**
     * @param prefix the prefix to set
     */
    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    /**
     * @param discovery the discovery to set
     */
    public void setDiscovery(ServerRestServiceDiscovery discovery) {
        this.discovery = discovery;
    }
}
